package org.example.redisson;

import cn.hutool.core.lang.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @Classname RedisLockImpl
 * @Description 封装redis分布锁加锁解锁
 * @Date 2021/3/24 4:16 下午
 * 假设我们提交订单的方法中，首先使用RedisLock接口对代码块添加了分布式锁，在加锁后的代码中调用了服务A，
 * 而服务A中也存在调用RedisLock接口的加锁和解锁操作。而多次调用RedisLock接口的加锁操作时，只要之前的锁没有失效，
 * 则会直接返回true，表示成功获取锁。也就是说，无论调用加锁操作多少次，最终只会成功加锁一次。而执行完服务A中的逻辑后，
 * 在服务A中调用RedisLock接口的解锁方法，此时，会将当前线程所有的加锁操作获得的锁全部释放掉。
 * @Created by xiangjd
 */
public class RedisLockImpl2 implements RedisLock {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //把加锁和解锁放在同一个线程里,防止不同线程之间串改
    private ThreadLocal<String> threadLocal = new ThreadLocal<String>();

    private ThreadLocal<Integer> threadLocalInteger = new ThreadLocal<Integer>();

    @Override
    public boolean tryLock(String key, long timeout, TimeUnit unit) {
        boolean isLock = false;
        if (threadLocal.get() == null) {
            String uuid = UUID.randomUUID().toString();
            threadLocal.set(uuid);
            isLock = stringRedisTemplate.opsForValue().setIfAbsent(key, uuid, timeout, unit);
        } else {
            isLock = true;
        }
        if(isLock){
             Integer count =  threadLocalInteger.get() == null?0:threadLocalInteger.get();
             threadLocalInteger.set(count++);
        }
        return isLock;
    }

    @Override
    public void releaseLock(String key) {
        //当前线程的uuid和redis中的uuid一样才能进行删除
        if (threadLocal.get().equals(stringRedisTemplate.opsForValue().get(key))) {
            Integer count = threadLocalInteger.get();
            //计数器减为0时释放锁
            if(count == 0 || --count <=0){
                stringRedisTemplate.delete(key);
            }

        }
    }
}
